import pako from 'pako';
import { Logger, ILogObj } from 'tslog';

declare interface Params {
  userId: string;
  maxCount?: number;
  maxInterTime?: number;
  needUpload?: boolean;
  uploadUrl?: string;
}

declare interface ConfigParams {
  name: string;
  userId: string;
  maxCount: number;
  maxInterTime: number;
  needUpload: boolean;
  uploadUrl: string;
}

declare interface Log {
  userId: string;
  name: string;
  level?: string;
  content?: string;
  date?: string;
  browerType?: string;
  deviceType?: string;
  interface?: string;
  data?: any;
}

class clientLogtail {
  private _logger: Logger<ILogObj>;
  private logTotalArr: Log[];
  private params: Params;
  private configParams: ConfigParams;
  private lastUploadTime: number;
  private retryCount: number;
  private isUploadBaseInfo: boolean;
  constructor(name: string, params: Params) {
    if (!name) {
      throw new Error('项目标识为必填项');
    }
    if (!params.userId) {
      throw new Error('用户Id为必填项');
    }
    //创建一个新的Logger实例
    this._logger = new Logger({
      name,
      hideLogPositionForProduction: true, // 优化日志性能
      stylePrettyLogs: true,
      prettyLogTimeZone: 'local', //日志输出的时区与本地时区一致  UTC时区和本地差8
    });
    this.logTotalArr = []; //log存储总数据
    this.params = params;
    this.configParams = {
      name: name, //产品标识
      userId: this.params.userId, //用户唯一标识
      maxCount: this.params.maxCount || 20, //最大日志数量
      maxInterTime: this.params.maxInterTime || 1000 * 60, //最大间隔时间(毫秒)
      needUpload: this.params.needUpload || false, //是否需要上传
      uploadUrl: this.params.uploadUrl as string, //上传地址
    };
    this.lastUploadTime = new Date().getTime();
    this.retryCount = 0; // 请求失败的重试次数
    // 是否上传过base信息(浏览器信息+deviceType+module)
    this.isUploadBaseInfo = false;
    const baseInfo = {
      name: '项目基础信息',
      userId: this.configParams.userId,
      content: JSON.stringify({
        name: name, //项目唯一标识
        browerType: navigator.userAgent,
        deviceType: this.getDeviceType(),
      }),
    };
    if (!this.isUploadBaseInfo) {
      this.logTotalArr.push(baseInfo);
      this.isUploadBaseInfo = true;
    }
    //启动定时器
    this.startUploadTimer();
    // 在日志记录时调用发送日志到后台服务器的函数
    this._logger.attachTransport(logData => {
      const { logLevelName, date } = logData._meta;
      let formatted: Log = {
        userId: this.params.userId,
        name: this.configParams.name,
        level: logLevelName,
        date: this.timeFormat(date),
        content: this.concatenateValues(logData),
      };
      this.logTotalArr.push(formatted);
      this.checkAndUploadLogs();
    });
  }
  //1 minute定时器
  startUploadTimer() {
    setTimeout(() => {
      if (this.logTotalArr.length) {
        this.checkAndUploadLogs();
      }
      this.startUploadTimer();
    }, 1000 * 60);
  }
  //获取设备类型
  getDeviceType(): string {
    const u = navigator.userAgent;
    let isAndroid = u.indexOf('Android') > -1 || u.indexOf('Adr') > -1; //android终端
    let isiOS = !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/); //ios终端
    // let isWechat = u.toLowerCase().indexOf('micromessenger') != -1; //微信浏览器
    if (isiOS) {
      return 'ios';
    }
    if (isAndroid) {
      return 'android';
    }
    return 'PC';
  }
  //检查是否达到了日志上传的条件
  checkAndUploadLogs() {
    if (this.logTotalArr.length >= this.configParams.maxCount || new Date().getTime() - this.lastUploadTime >= this.configParams.maxInterTime) {
      //达到20条或者距离上次上传时间超过1分钟
      if (this.configParams.needUpload) {
        this.compressIntoGzip(this.logTotalArr);
      } else {
        this.saveLogsToLocal(this.logTotalArr);
      }
      //上传完之后清空
      this.logTotalArr = [];
      //更新上传时间
      this.lastUploadTime = new Date().getTime();
    }
  }
  //压缩成gzip
  compressIntoGzip(logs: Log[]) {
    // 将日志数据转换为Uint8Array格式
    // const uint8Data = new TextEncoder().encode(JSON.stringify(logs))
    // 使用pako进行gzip压缩
    const compressedData = pako.gzip(JSON.stringify(logs));
    // 创建Blob对象用于保存压缩后的数据
    const blob = new Blob([compressedData], { type: 'application/gzip' });
    //压缩完成  调接口上传给后台
    if (blob.size / 1024 / 1024 > 2) {
      //大于2M
      alert('请合理设置最大日志上传数量');
    } else {
      this.uploadLogToserver(blob);
    }
  }
  //调用后台接口上传日志
  uploadLogToserver(blob: Blob) {
    let xhr = new XMLHttpRequest();
    xhr.open('POST', `${this.configParams.uploadUrl}`, true);
    xhr.setRequestHeader('Content-Type', 'application/gzip');
    // xhr.setRequestHeader("Access-Control-Allow-Origin", "*");
    // xhr.setRequestHeader("Authorization", "Bearer ");
    xhr.send(blob);
    xhr.onreadystatechange = () => {
      if (xhr.readyState == 4) {
        //请求操作已完成
        if (xhr.status == 200) {
          //成功响应
          try {
            const res = JSON.parse(xhr.responseText);
            if (res.code == 0) {
              console.log('成功');
            } else {
              alert(res.message);
            }
          } catch (e) {
            console.error(e);
          }
        } else {
          //请求失败
          if (this.retryCount < 1) {
            // 仅在重试次数小于1时重试
            this.reTryUpload(blob);
          } else {
            this.retryCount = 0;
          }
        }
      }
    };
  }

  //尝试请求一次
  reTryUpload(blob: Blob) {
    // 增加重试次数计数
    this.retryCount++;
    // 重试上传日志
    this.uploadLogToserver(blob);
  }

  //时间格式转换
  timeFormat(time: Date) {
    const date = new Date(time);
    const year = date.getFullYear();
    let month = date.getMonth() + 1;
    if (month < 10) {
      month = Number('0' + month);
    }
    let day = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
    let hour = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
    let minute = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
    let second = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
    return `${year}-${month}-${day} ${hour}:${minute}:${second}`;
  }

  //content格式转换
  concatenateValues(obj: any) {
    // 初始化结果字符串
    let result = '';
    // 遍历对象的属性
    for (const key in obj) {
      // 确保属性是对象自身的属性，而不是从原型链继承来的
      if (obj.hasOwnProperty(key)) {
        // 获取当前属性值
        const value = obj[key];
        // 如果属性值是对象且不为null，则将其用大括号括起来
        const formattedValue = typeof value === 'object' && value !== null ? ` ${JSON.stringify(value)} ` : value;
        // 拼接属性值到结果字符串中
        result += formattedValue + ' ';
      }
    }
    // 返回拼接后的结果字符串（去除末尾空格）
    return result.trim();
  }

  // 保存到本地
  saveLogsToLocal(logs: Log[]) {
    // 将日志数据转换为字符串
    const logData = JSON.stringify(logs);
    // 创建一个 Blob 对象
    const blob = new Blob([logData], { type: 'text/plain;charset=utf-8' });
    // 创建一个可下载的 URL
    const url = URL.createObjectURL(blob);
    // 创建一个 a 标签
    const link = document.createElement('a');
    // 设置 a 标签的 href 属性为可下载的 URL
    link.href = url;
    // 设置 a 标签的 download 属性为文件名
    link.download = 'logs.txt';
    // 将 a 标签添加到文档中
    document.body.appendChild(link);
    // 触发 a 标签的点击事件，开始下载
    link.click();
    // 下载完成后，从文档中移除 a 标签
    document.body.removeChild(link);
  }

  silly(...args: any[]) {
    this._logger.silly(...args);
  }

  trace(...args: any[]) {
    this._logger.trace(...args);
  }

  debug(...args: any[]) {
    this._logger.debug(...args);
  }

  info(...args: any[]) {
    this._logger.info(...args);
  }

  warn(...args: any[]) {
    this._logger.warn(...args);
  }

  error(...args: any[]) {
    this._logger.error(...args);
  }

  fatal(...args: any[]) {
    this._logger.fatal(...args);
  }

  //主动触发上传
  activeUpload() {
    if (this.logTotalArr.length) {
      this.compressIntoGzip(this.logTotalArr);
      this.logTotalArr = [];
    }
  }
}

export default clientLogtail;
